04. Two DoF Arm

Two Degrees of Freedom

Building a two-degree-of-freedom serial manipulator

You've now seen how a system of two points can be constrained down to just one degree of freedom (DoF) by connecting the points with a rod and anchoring the system at one end. Now, let's add a third point and a second DoF to build a robotic arm! Our system now looks like the image above, where p0 is the base of the two-link arm.

Constraints on a 2-DoF serial manipulator

Which quantities need to be specified as constraints to make this arm a 2-DoF system?

SOLUTION: The position of p0 and the length of the links connecting p0 to p1 and p1 to p2

Here is our 2-DoF manipulator with all the parts labeled.  Let's assume the base (Joint-0) is fixed at the origin (0, 0) of our coordinate system.

Here is our 2-DoF manipulator with all the parts labeled. Let's assume the base (Joint-0) is fixed at the origin (0, 0) of our coordinate system.

Additional parameters

With the constraints in place, which two additional parameters could be given to completely describe the configuration of the system at any time?

SOLUTION: The angle of Link-1 away from the x-axis and the angle of Link-2 from the Link-1 axis

Coding it up:

Now it's time to code up the 2-DoF arm! This will be a test of your intuition (and knowledge of trigonometry). Let's assume the position of the base of the arm (p0 above) is located at the origin (0, 0). Then once you have the length of the two links, all you need are the angles of the joints to completely specify the configuration of the arm.

In the quiz below, your job is to write a function that takes as inputs the length of each of the two links, and the joint angles, and outputs the (x, y) position of the joint at p1 and the end effector at p2. So it will look like this:

# Define a function to compute the arm configuration
# NOTE: joint1_angle is the angle counterclockwise from the link1 axis
def compute_arm_config(link1_length, link2_length, joint0_angle, joint1_angle):
    # TODO: compute the (x, y) position of the p1 joint and the end effector at p2.  
    #  joint1_x = 
    #  joint1_y =
    #  p2_x =
    #  p2_y =
    return joint1_x, joint1_y, p2_x, p2_y

When you succeed you will have solved the forward kinematics problem for a 2-DoF arm on a plane!

Start Quiz:

import matplotlib.pyplot as plt
import numpy as np

# Define a function to compute the arm configuration
def compute_arm_config(link1_length, link2_length, joint0_angle, joint1_angle):
    # TODO: compute the (x, y) position of the p1 joint and the end effector at p2.  
    joint1_x = 0
    joint1_y = 0
    p2_x = 0
    p2_y = 0
    return joint1_x, joint1_y, p2_x, p2_y
    
# Generate random link lengths and joint angles
# Note: because these are randomly generated on each run
# Every time you run the code you'll get a different result!
link1_length = np.random.random() * 30 + 20
link2_length = np.random.random() * 30 + 20
joint0_angle = np.random.random() * 2 * np.pi
joint1_angle = np.random.random() * 2 * np.pi

joint1_x, joint1_y, p2_x, p2_y = compute_arm_config(link1_length, link2_length, joint0_angle, joint1_angle)

print("joint0_angle =", round(joint0_angle * 180 / np.pi, 1), "degrees") 
print("joint1_angle =", round(joint1_angle * 180 / np.pi, 1),"degrees") 
print("End Effector at x =", round(p2_x, 1),"y =", round(p2_y, 1))
base_x = 0
base_y = 0
# Plot the links
plt.plot([base_x, joint1_x, p2_x], [base_y, joint1_y, p2_y])
# Plot the base as a blue square
plt.plot(base_x, base_y, 'bs', markersize=15, label='Base')
# Plot Joint-1 as a red circle
plt.plot(joint1_x, joint1_y, 'ro', markersize=15, label='Joint-1')
# Plot End Effector as a green triangle
plt.plot(p2_x, p2_y, 'g^', markersize=15, label='End Effector')
plt.xlim(-100, 100)
plt.ylim(-100, 100)
plt.legend(fontsize=15)
#plt.show() Uncomment to run locally
# Define a function to compute the arm configuration
def compute_arm_config(link1_length, link2_length, joint0_angle, joint1_angle):
    # TODO: compute the position of the p1 joint and the end effector at p2.        
    joint1_x = link1_length * np.cos(joint0_angle)
    joint1_y = link1_length * np.sin(joint0_angle)
    p2_x = joint1_x + link2_length * np.cos(joint0_angle + joint1_angle)
    p2_y = joint1_y + link2_length * np.sin(joint0_angle + joint1_angle)
    return joint1_x, joint1_y, p2_x, p2_y
    

Proof

Still struggling to solve this system? if yes, we will draw the free-body diagram of this system and work out the steps leading toward the solution. Before you start, take a look at the Trigonometry with right triangles tutorials to refresh your math skills.

At the local frame 1, solving the position of P1 with respect to the global frame at P0:

x_{1} = l_{1}\cos(\Theta_{1})
y_{1} = l_{1}\sin(\Theta_{1})

At the local frame 2, solving the position of P2 with respect to the reference frame at P1:

x_{2} = l_{2}\cos(\Theta_{1}+\Theta_{2})
y_{2} = l_{2}\sin(\Theta_{1}+\Theta_{2})

Finally, putting it all together and solving the position of the end effector P2 with respect to the global frame at P0:

x_{2} = l_{1}\cos(\Theta_{1}) +\ l_{2}\cos(\Theta_{1}+\Theta_{2})
y_{2} = l_{1}\sin(\Theta_{1}) +\ l_{2}\sin(\Theta_{1}+\Theta_{2})